# algorithm/models/user_tag_mastery.py
from django.db import models
from django.contrib.auth.models import User
from recommendation.models import SolvedProblem
from . import TagValue, TagDependency
from ..utils import mastery_level1, diff_score, mastery_level2, self_contribution_recommendation, \
    inverted_mastery_level1, extract_algorithm_tag_indices, mastery_level3
from users.models import User as CustomUser  # 自定义的User表
from tag.models import AlgorithmTag  # 改为引用 AlgorithmTag
from problems.models import Problem
import logging
logger = logging.getLogger('algorithm')


# 用户标签模型
class UserTagMastery(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    tag = models.ForeignKey(AlgorithmTag, on_delete=models.CASCADE)  # 使用 AlgorithmTag 替代 Tag
    mastery_score = models.FloatField(default=0.0)
    mastery_level = models.FloatField(default=0.0)
    recommendation_score = models.FloatField(default=0.0)

    class Meta:
        unique_together = ('user', 'tag')

    def __str__(self):
        return f"{self.user.username} - Tag {self.tag.tag_name}"

    def save_state(self):
        return {
            'tag_index': self.tag.index,
            'mastery_score': self.mastery_score,
            'mastery_level': self.mastery_level,
            'recommendation_score': self.recommendation_score
        }

    @staticmethod
    def restore_state(user, state_data_list):
        for state_data in state_data_list:
            tag = AlgorithmTag.objects.get(index=state_data['tag_index'])
            obj, created = UserTagMastery.objects.update_or_create(
                user=user,
                tag=tag,
                defaults=state_data
            )
    # 以下计算函数均只计算数据库中数据,只管计算，不管所需数据是否为最新/是否已计算
    # 计算用户标签掌握分
    # 希望实现八个方法,计算逻辑均集中在calculate的类静态方法中
    # calculate_mastery_score(cls, user_id, tag_index)
    # calculate_mastery_score(self)
    # create_or_update_mastery_score(cls, user_id, tag_index)
    # create_or_update_mastery_score(self)，
    #
    # calculate_mastery_level(cls, user_id, tag_index)
    # calculate_mastery_level(self)
    # create_or_update_mastery_level(cls, user_id, tag_index)
    # create_or_update_mastery_level(self)

    # 若mastery_score已计算/及时计算则该函数执行的是有效行为，否则无效；该函数也没有保证mastery_score已计算/及时计算的责任
    @classmethod
    def calculate_mastery_score(cls, user_id, tag_index):
        try:
            # 尝试获取用户和标签实例，如果不存在则抛出 DoesNotExist 异常
            user = User.objects.get(id=user_id)
            tag = AlgorithmTag.objects.get(index=tag_index)  # 使用 AlgorithmTag
        except User.DoesNotExist:
            print(f"No user found with id {user_id}")
            return None
        except AlgorithmTag.DoesNotExist:
            print(f"No tag found with index {tag_index}")
            return None
        # 直接通过 SolvedProblem 获取已解决且属于指定标签的题目
        solved_problems = SolvedProblem.objects.filter(user=user, problem__tags=tag).select_related('problem')

        total_value = 0.0
        for solved_problem in solved_problems:
            problem = solved_problem.problem
            algorithm_tags_count = problem.tags.filter(
                index__in=AlgorithmTag.objects.values_list('index', flat=True)).count()
            if problem.difficulty_index is not None and problem.difficulty_index < len(diff_score):
                score = diff_score[problem.difficulty_index] / algorithm_tags_count
                total_value += score
        return total_value

    # 类方法：计算 mastery_level
    @classmethod
    def calculate_mastery_level(cls, user_id, tag_index):
        try:
            # 获取用户、标签及掌握分数
            user = User.objects.get(id=user_id)
            tag = AlgorithmTag.objects.get(index=tag_index)
            mastery_score = cls.objects.get(user=user, tag=tag).mastery_score
        except (User.DoesNotExist, AlgorithmTag.DoesNotExist, cls.DoesNotExist):
            print(f"Either User, Tag, or Mastery entry not found for user_id={user_id} and tag_index={tag_index}")
            return None

        try:
            # 获取标签总分
            tag_value = TagValue.objects.get(tag=tag).tag_value
            if tag_value == 0:
                print(f"Tag value is zero for tag_index={tag_index}, avoiding division by zero.")
                return None  # 或返回一个特定的低掌握级别或错误码
        except TagValue.DoesNotExist:
            print(f"TagValue does not exist for tag_index={tag_index}.")
            return None

        # 计算提高因子
        if tag_value > 3000:
            increase_factor = 1.0 + ((tag_value - 3000) / 7000) * 1  # 提高因子在 1.2 到 2 之间线性变化
            increase_factor = min(increase_factor, 2.0)
        else:
            increase_factor = 1.0

        # 计算分子并避免超出标签总分
        adjusted_score = min(tag_value, mastery_score * increase_factor)

        # 返回掌握度，使用修正后的分子除以标签总分
        return mastery_level2(adjusted_score / tag_value)

    def calculate_mastery_score_self(self):
        return self.__class__.calculate_mastery_score(self.user.id, self.tag.index)

    @classmethod
    def create_or_update_mastery_score(cls, user_id, tag_index):
        new_mastery_score = cls.calculate_mastery_score(user_id, tag_index)
        try:
            user = User.objects.get(id=user_id)
            tag = AlgorithmTag.objects.get(index=tag_index)  # 使用 AlgorithmTag
        except User.DoesNotExist:
            print(f"User with id {user_id} does not exist.")
            logger.error(f"{cls.__name__}.create_or_update_mastery_score : User with id{user_id} does not exist.")
            return None, False
        except AlgorithmTag.DoesNotExist:
            print(f"Tag with index {tag_index} does not exist.")
            logger.error(f"{cls.__name__}.create_or_update_mastery_score : Tag with index {tag_index} does not exist.")
            return None, False

        user_tag_mastery, created = cls.objects.update_or_create(
            user=user,
            tag=tag,
            defaults={'mastery_score': new_mastery_score}
        )
        logger.debug(f"{cls.__name__}.create_or_update_mastery_score user{user_id} tag_index{tag_index} Success")
        return user_tag_mastery, created

    def create_or_update_mastery_score_self(self):
        new_mastery_score = self.calculate_mastery_score_self()
        self.mastery_score = new_mastery_score
        self.save()
        return self  # 返回更新后的实例

    # 实例方法：计算 mastery_level，调用类静态方法
    def calculate_mastery_level_self(self):
        return self.calculate_mastery_level(self.user.id, self.tag.index)

    # 类方法：创建或更新 mastery_level
    @classmethod
    def create_or_update_mastery_level(cls, user_id, tag_index):
        calculated_mastery_level = cls.calculate_mastery_level(user_id, tag_index)

        if calculated_mastery_level is None:
            print(f"Failed to calculate mastery level for user_id={user_id} and tag_index={tag_index}")
            logger.error(f"{cls.__name__}.create_or_update_mastery_level : Failed to calculate mastery level for user_id={user_id} and tag_index={tag_index}")
            return None, False

        try:
            user = User.objects.get(id=user_id)
            tag = AlgorithmTag.objects.get(index=tag_index)  # 使用 AlgorithmTag

            user_tag_mastery, created = cls.objects.update_or_create(
                user=user,
                tag=tag,
                defaults={'mastery_level': calculated_mastery_level}
            )
            logger.debug(f"{cls.__name__}.create_or_update_mastery_level user{user_id} tag_index{tag_index} Success")
            return user_tag_mastery, created
        except User.DoesNotExist:
            print(f"User with id {user_id} does not exist.")
            logger.error(f"{cls.__name__}.create_or_update_mastery_level : User with id{user_id} does not exist.")
            return None, False
        except AlgorithmTag.DoesNotExist:
            print(f"Tag with index {tag_index} does not exist.")
            logger.error(f"{cls.__name__}.create_or_update_mastery_level : Tag with index {tag_index} does not exist.")
            return None, False
        except Exception as e:
            print(f"Error updating or creating mastery level: {e}")
            logger.error(f"{cls.__name__}.create_or_update_mastery_level : Error updating or creating mastery level: {e}")
            return None, False

    # 实例方法：创建或更新 mastery_level
    def create_or_update_mastery_level_self(self):
        calculated_mastery_level = self.calculate_mastery_level_self()
        if calculated_mastery_level is not None:
            self.mastery_level = calculated_mastery_level
            self.save()
            print(
                f"Mastery level updated to {calculated_mastery_level} for user {self.user.username} and tag {self.tag.tag_name}")
        else:
            print(f"Failed to update mastery level for user {self.user.username} and tag {self.tag.tag_name}")

    #
    # 标签推荐分
    #
    @classmethod
    def calculate_recommendation_score(cls, user_id, tag_index):
        try:
            # 获取用户和标签实例
            user = User.objects.get(id=user_id)
            tag = AlgorithmTag.objects.get(index=tag_index)  # 使用 AlgorithmTag
            # 获取当前标签的掌握度
            mastery_entry = cls.objects.get(user=user, tag=tag)
            mastery_level = mastery_entry.mastery_level
            # 初始化推荐分，自身推荐分，由本身标签的掌握度决定，是一个下降函数
            recommendation_score = self_contribution_recommendation(mastery_level)
            # 获取与此标签相关的依赖关系
            dependencies = TagDependency.objects.filter(target_tag=tag)
            # 遍历所有依赖关系，累加贡献分
            for dependency in dependencies:
                source_tag_mastery = cls.objects.get(user=user, tag=dependency.source_tag)
                recommendation_score += source_tag_mastery.mastery_level * dependency.weight
            # 乘自身标签掌握度对应的比例，掌握度越高推荐比例越低
            recommendation_score = recommendation_score * inverted_mastery_level1(mastery_level)
            # logger.debug(
            #     f"{cls.__name__}.calculate_recommendation_score user{user_id} tag_index{tag_index} Success:recommendation_score {recommendation_score}")
            return recommendation_score

        except (User.DoesNotExist, AlgorithmTag.DoesNotExist, cls.DoesNotExist):
            print(f"User or Tag or Mastery entry not found for user_id={user_id} and tag_index={tag_index}")
            logger.error(f"{cls.__name__}.calculate_recommendation_score : User or Tag or Mastery entry not found for user_id={user_id} and tag_index={tag_index}")
            return None

    @classmethod
    def create_or_update_recommendation_score(cls, user_id, tag_index):
        recommendation_score = cls.calculate_recommendation_score(user_id, tag_index)
        if recommendation_score is None:
            return None, False
        # 获取 user 和 tag 对象
        try:
            user = User.objects.get(id=user_id)
            tag = AlgorithmTag.objects.get(index=tag_index)  # 使用 AlgorithmTag
        except User.DoesNotExist:
            print(f"User with id {user_id} does not exist.")
            logger.error(f"{cls.__name__}.create_or_update_recommendation_score : User with id{user_id} does not exist.")
            return None, False
        except AlgorithmTag.DoesNotExist:
            print(f"Tag with index {tag_index} does not exist.")
            logger.error(f"{cls.__name__}.create_or_update_recommendation_score : Tag with index {tag_index} does not exist.")
            return None, False
        # 使用 update_or_create 来更新或创建 UserTagMastery 实例
        user_tag_mastery, created = cls.objects.update_or_create(
            user=user,
            tag=tag,
            defaults={'recommendation_score': recommendation_score}
        )
        logger.debug(f"{cls.__name__}.create_or_update_recommendation_score user{user_id} tag_index{tag_index} Success")
        return user_tag_mastery, created

    def calculate_recommendation_score_self(self):
        return self.__class__.calculate_recommendation_score(self.user.id, self.tag.index)

    def create_or_update_recommendation_score_self(self):
        recommendation_score = self.calculate_recommendation_score_self()
        if recommendation_score is not None:
            self.recommendation_score = recommendation_score
            self.save()
            print(
                f"Recommendation score updated to {self.recommendation_score} for user {self.user.username} and tag {self.tag.tag_name}")
        else:
            print(f"Failed to update recommendation score for user {self.user.username} and tag {self.tag.tag_name}")

    # 下面两个方法没什么应用场景
    @classmethod
    def create_or_update_all_fields(cls, user_id, tag_index):
        # 类静态方法：通过 user_id 和 tag_index 计算并更新 mastery_score, mastery_level 和 recommendation_score
        cls.create_or_update_mastery_score(user_id, tag_index)
        cls.create_or_update_mastery_level(user_id, tag_index)
        cls.create_or_update_recommendation_score(user_id, tag_index)

    def create_or_update_all_fields_self(self):
        # 实例方法：计算并更新当前实例的 mastery_score, mastery_level 和 recommendation_score
        self.create_or_update_mastery_score_self()
        self.create_or_update_mastery_level_self()
        self.create_or_update_recommendation_score_self()
        self.save()
        return self  # 返回更新后的实例

    # 下面三个方法是有用的，在初始化时分层计算
    # 计算 mastery_score 的类静态方法
    @classmethod
    def initialize_user_mastery_scores(cls, user_id):
        try:
            user = User.objects.get(id=user_id)
            tags = AlgorithmTag.objects.all()  # 使用 AlgorithmTag

            for tag in tags:
                cls.create_or_update_mastery_score(user_id, tag.index)

            print(f"Mastery scores calculated for all tags for user_id={user_id}")
            logger.info(f"{cls.__name__}.initialize_all_user_mastery_score for user_id={user_id} Success")
        except User.DoesNotExist:
            print(f"No user found with id {user_id}")
            logger.error(f"{cls.__name__}.initialize_all_user_mastery_score : No user found with id {user_id}")
        except Exception as e:
            print(f"An error occurred while calculating mastery scores for user_id={user_id}: {e}")
            logger.error(f"{cls.__name__}.initialize_all_user_mastery_score : An error occurred while calculating mastery scores for user_id={user_id}:{e}")

    # 计算 mastery_level 的类静态方法
    @classmethod
    def initialize_user_mastery_levels(cls, user_id):
        try:
            user = User.objects.get(id=user_id)
            tags = AlgorithmTag.objects.all()  # 使用 AlgorithmTag

            for tag in tags:
                cls.create_or_update_mastery_level(user_id, tag.index)

            print(f"Mastery levels calculated for all tags for user_id={user_id}")
            logger.info(f"{cls.__name__}.initialize_user_mastery_levels for user_id={user_id}Success")
        except User.DoesNotExist:
            print(f"No user found with id {user_id}")
            logger.error(f"{cls.__name__}.initialize_user_mastery_levels : No user found with id {user_id}")
        except Exception as e:
            print(f"An error occurred while calculating mastery levels for user_id={user_id}: {e}")
            logger.error(f"{cls.__name__}.initialize_user_mastery_levels : An error occurred while calculating mastery levels for user_id={user_id}:{e}")

    # 计算 recommendation_score 的类静态方法
    @classmethod
    def initialize_user_recommendation_scores(cls, user_id):
        try:
            user = User.objects.get(id=user_id)
            tags = AlgorithmTag.objects.all()  # 使用 AlgorithmTag

            for tag in tags:
                cls.create_or_update_recommendation_score(user_id, tag.index)

            print(f"Recommendation scores calculated for all tags for user_id={user_id}")
            logger.info(f"{cls.__name__}.initialize_user_recommendation_scores for user_id={user_id} Success")
        except User.DoesNotExist:
            print(f"No user found with id {user_id}")
            logger.error(f"{cls.__name__}.initialize_user_recommendation_scores : No user found with id {user_id}")
        except Exception as e:
            print(f"An error occurred while calculating recommendation scores for user_id={user_id}: {e}")
            logger.error(f"{cls.__name__}.initialize_user_recommendation_scores : An error occurred while calculating recommendation scores for user_id={user_id}:{e}")

    # 上面三个方法的统一调用，蕴含了调用顺序
    @classmethod
    def initialize_user_tag_mastery(cls, user_id):
        try:
            # 先计算 mastery_score
            cls.initialize_user_mastery_scores(user_id)
            # 再计算 mastery_level
            cls.initialize_user_mastery_levels(user_id)
            # 最后计算 recommendation_score
            cls.initialize_user_recommendation_scores(user_id)

            print(f"Successfully initialized all tags for user_id={user_id}")

        except User.DoesNotExist:
            print(f"No user found with id {user_id}")
        except Exception as e:
            print(f"An error occurred while initializing tags for user_id={user_id}: {e}")
    # 清空用户UserTagMastery
    @classmethod
    def clear_user_tag_mastery(cls, user_id):
        try:
            # 查找对应用户
            user = User.objects.get(id=user_id)

            # 删除该用户在 UserTagMastery 中的所有记录
            # 用 _ 来忽略不需要注意的值
            deleted, _ = cls.objects.filter(user=user).delete()

            print(f"Successfully deleted {deleted} records for user_id={user_id}")

        except User.DoesNotExist:
            print(f"No user found with id {user_id}")
        except Exception as e:
            print(f"An error occurred while deleting records for user_id={user_id}: {e}")

    # 使用新增的过题集合对UserTagMastery进行更新
    @classmethod
    def update_user_tag_mastery_for_problems(cls, user_id, problems):
        try:
            # 使用工具函数从问题集合中提取算法标签索引
            union_tag_indices = extract_algorithm_tag_indices(problems)

            # 计算所有相关标签的 mastery_score
            for tag_index in union_tag_indices:
                cls.create_or_update_mastery_score(user_id, tag_index)

            # 计算所有相关标签的 mastery_level
            for tag_index in union_tag_indices:
                cls.create_or_update_mastery_level(user_id, tag_index)

            # 计算所有相关标签的 recommendation_score
            for tag_index in union_tag_indices:
                cls.create_or_update_recommendation_score(user_id, tag_index)

            print(f"Successfully updated mastery details for user_id={user_id}")
            logger.info(f"{cls.__name__}.update_user_tag_mastery_for_problems for user_id={user_id} Success")
        except Exception as e:
            print(f"An error occurred while updating tag mastery for user_id={user_id}: {e}")
            logger.error(f"{cls.__name__}.update_user_tag_mastery_for_problems : An error occurred while updating tag mastery for user_id={user_id}:{e}")

    @classmethod
    def print_user_tag_mastery_info(cls, user_id):
        # 尝试获取与user_id关联的所有UserTagMastery记录
        try:
            user = User.objects.get(id=user_id)
            mastery_records = cls.objects.filter(user=user)

            print(f"Mastery information for user: {user.username}")
            print("------------------------------------------------")
            header = f"{'Tag Name':<30}{'Mastery Score':>15}{'Mastery Level':>15}{'Recommendation Score':>20}"
            print(header)
            print('-' * len(header))

            for record in mastery_records:
                print(f"{record.tag.tag_name:<30}{record.mastery_score:>15.2f}{record.mastery_level:>15.2f}{record.recommendation_score:>20.2f}")

        except User.DoesNotExist:
            print(f"No user found with ID {user_id}")
        except Exception as e:
            print(f"An error occurred: {str(e)}")
# 删除了两个用于回溯的子类，决定使用备忘录模式完成